// main application object
var App = {};

// in acest obiect sunt stocate clasele care modeleaza anumite functionalitati generale
var Models = {};

(function(){


    ////////////////////////////////////////////////////////////////////////////////
    //===== CONFIRMATION MESSAGE ===================================================
    ////////////////////////////////////////////////////////////////////////////////

    var ConfirmationMessage = Module.extend(
    {
        _options:
        {
            // the message will automatically be hidden after this amount of miliseconds
            hideAfter: 3000,
            // hide the message on click anywhere in the document
            hideOnClick: true,
            // hide with fadeOut effect; this option specifies the effect duration
            fadeOut: 500,
            errorClass: 'error',
            message: null
        },

        _$textContent: null,

        _timeout: null,

        _init: function()
        {
            var self = this;

            self._$textContent = self.getElement().find( '.content' );

            // set the default message
            self._setMessage( self._options.message );

            if ( self._options.hideOnClick )
            {
                // hide the message on click
                $.documentEvent( self.getElement(), function(){
                    self.hide();
                })
            }
        },

        hide: function( immediately )
        {
            var self = this,
                $element = this.getElement();

            // reset the timer
            clearTimeout( this._timeout );

            // hide the message
            immediately = ( typeof immediately != 'undefined' && immediately ) ?
                    true:
                    false;

            var hide = function(){
                $element
                    .hide()
                    .removeClass( self._options.errorClass );
            }

            if (immediately)
            {
                hide();
            }
            else
            {
                $element.fadeOut( this._options.fadeOut , hide );
            }
        },

        show: function( message, timeout, error )
        {
            var self = this;

            if ( (typeof error == 'undefined' || !error) && (timeout === true) )
            {
                error = true;
                timeout = null;
            }

            self.hide();

            // set the text message
            this._setMessage( message );

            // show the message
            this.getElement().show();

            // check if the message is an error
            if ( typeof error != 'undefined' && error )
            {
                this.getElement().addClass(this._options.errorClass);
            }

            // hide it automatically
            if ( this._options.hideAfter )
            {
                this._timeout = setTimeout( function(){
                        self.hide();
                    },
                    (typeof timeout != 'undefined' && timeout) || this._options.hideAfter );
            }
        },

        showError: function( message )
        {
            this.show( message, 10000, true );
        },

        _setMessage: function( message )
        {
            if ( typeof message != 'undefined' && message != null )
            {
                this._$textContent.text( message );
            }
        }

    });

    ////////////////////////////////////////////////////////////////////////////////
    //===== OVERLAY ================================================================
    ////////////////////////////////////////////////////////////////////////////////

    var Overlay = Module.extend(
    {
        _init: function()
        {
            this.disable();
        },

        enable: function()
        {
            this.getElement().show();
        },

        disable: function()
        {
            this.getElement().hide();
        }
    });


    ////////////////////////////////////////////////////////////////////////////////
    //===== FORM ===================================================================
    ////////////////////////////////////////////////////////////////////////////////

    /**
    * Model for a general ajax form
    * The constructor takes the form element object as the first argument
    *
    * HTML DEFINITION: <form id="..." request="[url]" action="javascript:void(0)" autocomplete="off">
    *
    * Methods:
    *    - submit
    *    - reset
    *    - getField
    *    - getFields
    *    - getFieldValue
    *    - setFieldValue
    *    - getData
    *
    * Custom handlers (defined in derived classes)
    *    - _onBeforeSubmit
    *    - _onReset
    *    - _onSuccess
    *    - _onErrors
    */
    var Form = Module.extend(
    {
        _options:
        {
            timeout: 6000,
            requestingClass: 'requesting',
            errorContainerSelector: '.error-container',
            fieldRowSelector: '.field-row'
        },

        _formName: null,
        _$submitButton: null,
        _inProgress: false,

        _init: function()
        {
            var self = this,
                $element = self.getElement();

            // setup the form name
            self._formName = $element.attr( 'name' );

            // define custom submit handler
            $element.submit( function( event ){
                event.preventDefault();
                self.submit();
            });

            // find the submit button and save it
            self._$submitButton = $element.find( ':submit' );

            // define custom reset handler
            $element.reset( function(){
                self._reset();
                // trigger the 'change' event on all form fields
                // - the default 'reset' event is executed after the custom handlers;
                //   because of this we launch the 'change' event on a separate thread after a small delay
                setTimeout( function(){
                        self.getFields().change();
                    },
                    1);
            });
        },

        submit: function()
        {
            var self = this,
                $element = self.getElement();

            if ( self._inProgress ) return;

            self._start();

            // call custom 'beforeSubmit' handler
            self._onBeforeSubmit();

            $.ajax({
                url:        $element.attr('request'),
                timeout:    self.getOption('timeout'),
                async:      false,
                type:       'post',
                data:       self.getData(),
                success:    function( response, statusText, xhr )
                {
                    // reset errors
                    self._clearErrors();

                    // exista erori ?
                    var responseType = xhr.getResponseHeader( 'Response-Type' );
                    if ( responseType == 'validation-errors' )
                    {
                        self._renderErrors(response);
                        // call custom 'error' handler
                        self._onErrors(response);
                    }
                    // nu exista erori:
                    else if (response.success)
                    {
                        // reset the Widget
                        self.reset();

                        // call 'success' handler
                        self._onSuccess(response.data);
                    }
                    else
                    {
                        App.ConfirmationMessage.showError( 'Server error' );
                    }

                },
                complete: function( response, statusText, xhr )
                {
                    self._end();
                }
            });
        },

        reset: function()
        {
            this.getElement().reset();
        },

        /**
        * Returns a field as jQuery object
        * @param fieldName The value of 'name' attribute of the field
        * @return jQuery
        */
        getRawField: function( fieldName, filterSelector )
        {
            if ( typeof filterSelector == 'undefined' )
            {
                filterSelector = '';
            }

            var $field = this.getElement().find( '[name="' + fieldName + '"]' + filterSelector );

            return $field;
        },

        /**
        * Returns a field as jQuery object
        * @param string|array fieldName The value of 'name' attribute of the field, excluding the form name
        * @return jQuery
        */
        getField: function( fieldName, filterSelector )
        {
            if ( $.isArray(fieldName) )
            {
                fieldName = fieldName.join('][');
            }
            fieldName = this._formName + '[' + fieldName + ']';

            var $field = this.getRawField( fieldName, filterSelector );

            return $field;
        },

        /**
        * Sets the value of a field
        * @param field The value of 'name' attribute of the field
        * @param value The value to be set
        */
        setFieldValue: function( field, value )
        {
            // set the value and trigger the 'change' event
            this.getField( field )
                    .val( value )
                    .change();
        },

        /**
        * Gets the value of a field
        * @param field The value of 'name' attribute of the field
        */
        getFieldValue: function( field )
        {
            // set the value and trigger the 'change' event
            this.getField( field )
                    .val();
        },

        getFields: function()
        {
            var self = this,
                arrayData = self.getElement().serializeArray(),
                $fields = $();

            // there can be multiple fields having the same name attribute
            var fieldsCount = {};

            $.each( arrayData, function( index ){
                var fieldName = this.name;

                if ( typeof fieldsCount[fieldName] == 'undefined' )
                {
                    fieldsCount[fieldName] = 0;
                }

                var $field = self.getRawField( fieldName, ':eq(' + ( fieldsCount[fieldName]++ ) + ')' );

                $fields = $fields.add( $field );
            })

            return $fields;
        },

        /**
         * Return the active form fields
         *
         * @return jQuery
         */
        getDataFields: function()
        {
            var $fields = $();
            // remove values of not visible fields
            $.each(this.getFields(), function(){
                var $field = $(this);

                // if the field is marked as important OR hasn't any hidden parents, it is included in the result
                if ( $field.attr('_important') || !$field.parents(':hidden').length )
                {
                    $fields = $fields.add( $field );
                }
            })

            return $fields;

        },

        getData: function()
        {
            var data = [];

            this.getDataFields().each(function(){
                var $field      = $(this),
                    fieldName   = $field.attr('name'),
                    fieldValue  = $field.val();

                data.push({
                    name: fieldName,
                    value: fieldValue
                });

            })

            return data;
        },

        /**
        * These event handlers will be defined in derived classes
        */
        _onBeforeSubmit: function() {},
        _onReset: function() {},
        _onSuccess: function(responseData) {},
        _onErrors: function() {},

        /**
        * Marks the start of the request
        */
        _start: function()
        {
            this._$submitButton
                    .attr( 'disabled', 'disabled' )
                    .addClass( this.getOption('requestingClass') );

            App.Overlay.enable();
            App.ConfirmationMessage.hide( true );

            this._inProgress = true;
        },

        /**
        * Marks the end of the request
        */
        _end: function()
        {
            this._$submitButton
                    .removeAttr( 'disabled' )
                    .removeClass( this.getOption('requestingClass') );

            App.Overlay.disable();

            this._inProgress = false;
        },

        _clearErrors: function()
        {
            this.getElement().find( this.getOption('errorContainerSelector') ).html('');
        },

        _renderErrors: function( errors )
        {
            var self = this,
                $element = self.getElement();

            // display the validation error messages
            // daca a fost primit parametrul 'refresh', atunci se reincarca pagina
            if ( errors.refresh )
            {
                window.location.reload();
                throw new Error( 'Reloading the page!' );
            }

            // erori specifice unui camp din formular
            $.each( errors.fields, function( key ){
                // find error container; 'key' represents the value of the name attribute
                var $field = self.getRawField( key );
                $field.parents( self.getOption('fieldRowSelector') )
                        .find( self.getOption('errorContainerSelector') )
                        .text( this.valueOf() );
            });

            // erori globale (specifice formularului)
            var $globalErrors = $element.children( self.getOption('errorContainerSelector') );
            if ( $globalErrors.length )
            {
                $.each( errors.global, function(){
                    $globalErrors.append( this.valueOf() + '<br />' );
                });
            }

        },

        _reset: function()
        {
            this._clearErrors();
            this._onReset();
        }

    });

    ////////////////////////////////////////////////////////////////////////////////
    //===== TABS ===================================================================
    ////////////////////////////////////////////////////////////////////////////////

    var Tabs = Module.extend(
    {
        _init: function()
        {
            this.getElement().tabs( this.getOptions() );
        },

        /**
        * Select a tab
        */
        select: function( tabIndex )
        {
            this.getElement().tabs( 'select', tabIndex );
        }

    });

    ////////////////////////////////////////////////////////////////////////////
    // attach the defined models to Models global container ////////////////////
    ////////////////////////////////////////////////////////////////////////////

    Models = {
        ConfirmationMessage: ConfirmationMessage,
        Overlay: Overlay,
        Form: Form,
        Tabs: Tabs
    };

})()
